/**
 * @file Vector3.cpp
 * @author  Aljosa Osep <aljosa.osep@gmail.com, Fletcher Dunn, Ian Parnberry>
 * @version 1.0
 *
 * @section LICENSE
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:

 *  1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.

 *  2. Redistributions in binary form must reproduce the above copyright notice, this list
 *     of conditions and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.

 * THIS SOFTWARE IS PROVIDED BY <Aljosa Osep> ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <Aljosa Osep>> OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

 * The views and conclusions contained in the software and documentation are those of the
 * authors and should not be interpreted as representing official policies, either expressed
 * or implied, of <Aljosa Osep>.
 *
 * @section DESCRIPTION
 *
 * Vector3 - an 3D vector library impelentation
 * Based on examples from 3D Math Premier of Graphics and Games Development book
 * Original authors: Fletcher Dunn, Ian Parnberry. Thanks!
 */

#include "Vector3.h"

namespace xEngine
{
    namespace PlatformIndependent
    {
        namespace Math
        {

            // Constructors
            Vector3::Vector3() : x(0.0), y(0.0), z(0.0) { }  // default - zero vector
            Vector3::Vector3(const Vector3 &p) : x(p.x), y(p.y), z(p.z) { } // copy constructor
            Vector3::Vector3(float x, float y, float z) : x(x), y(y), z(z) { }

            // Operator: Assignment
            Vector3 &Vector3::operator=(const Vector3 &a)
            {
                this->x = a.x; this->y = a.y; this->z = a.z;
                return *this;
            }

            // Operator: Equality ( TODO: CHECK WITH DELTA, FLOATING VALUES! )
            bool Vector3::operator==(const Vector3 &p) const
            {
                return this->x == p.x && this->y == p.y && this->z == p.z;//((this->x == p.x) && (this->y == p.y) && (this->z == p.z)) ? true : false;
            }

            // Operator: Inequality ( TODO: CHECK WITH DELTA, FLOATING VALUES! )
            bool  Vector3::operator!=(const Vector3 &a) const
            {
                return this->x!=a.x || this->y!=a.y || this->z!=a.z;
            }


            // Vector operations
            void Vector3::zero() {
                this->x = this->y = this->z = 0.0f;
            }

            // Vector negative
            Vector3 Vector3::operator-() const
            {
                return Vector3(-x, -y, -z);
            }

            // Substraction
            Vector3 Vector3::operator-(const Vector3 &p) const {
                return Vector3(this->x - p.x, this->y - p.y, this->z - p.z);
            }

            // Addition
            Vector3 Vector3::operator+(const Vector3 &p) const {
                return Vector3(this->x + p.x, this->y + p.y, this->z + p.z);
            }

            // Scalar multiplication
            Vector3 Vector3::operator*(float a) const
            {
                return Vector3(this->x*a, this->y*a, this->z*a);
            }

            // Scalar division
            Vector3 Vector3::operator/(float a) const
            {
                // TODO: DELTA CHECK WITH FLOATS
                if (a != 0.0f)
                    return Vector3(this->x/a, this->y/a, this->z/a);
            }

            // Combined addition
            Vector3 &Vector3::operator +=(const Vector3 &a) {
                this->x += a.x;
                this->y += a.y;
                this->z += a.z;

                return *this;
            }

            // Combined substraction
            Vector3 &Vector3::operator -=(const Vector3 &a) {
                this->x -= a.x;
                this->y -= a.y;
                this->z -= a.z;

                return *this;
            }

            // Combined multiplication
            Vector3 &Vector3::operator *=(float a) {
                this->x *= a;
                this->y *= a;
                this->z *= a;

                return *this;
            }

            // Combined division
            Vector3 &Vector3::operator /=(float a) {
                if (a != 0.0f) {
                    this->x /= a;
                    this->y /= a;
                    this->z /= a;

                    return *this;
                }
            }

            // Dot product
            float Vector3::operator *(const Vector3 &a) const {
                return x*a.x + y*a.y + z*a.z;
            }

            // Length of this vector
            float Vector3::length()
            {
                // vector length = sqrt(x^2 + y^2 + z^2)
                return sqrt(x*x + y*y + z*z);
            }

            // Normalize this vector
            void Vector3::normalize()
            {
                float lenSq = x*x + y*y + z*z;
                if (lenSq > 0.0f) {
                    float oneOverLenSq = 1.0f / sqrt(lenSq);

                    this->x *= oneOverLenSq;
                    this->y *= oneOverLenSq;
                    this->z *= oneOverLenSq;
                }
            }

            // Distance to point
            float Vector3::distanceToPoint(const Vector3 &p) const {
                float dx = p.x - x;
                float dy = p.y - y;
                float dz = p.z - z;

                return sqrt( dx*dx + dy*dy + dz*dz );
            }

            // Setter
            void Vector3::set(float x, float y, float z)
            {
                this->x = x;
                this->y = y;
                this->z = z;
            }

            ////////////////////////////
            // NONMEMBER FUNCTIONS
            ////////////////////////////

            // Distance between two vectors
            inline float distance(const Vector3 &a, const Vector3 &b) {
                float dx = b.x - a.x;
                float dy = b.y - a.y;
                float dz = b.z - a.z;

                return sqrt( dx*dx + dy*dy + dz*dz );
            }

            // Length of vector (magnitude)
            inline float vectorLength(const Vector3 &a) {
                return sqrt(a.x*a.x + a.y*a.y + a.z*a.z);
            }

            // Cross product between two vectors
            inline Vector3 crossProduct(const Vector3 &a, const Vector3 &b) {
                return Vector3(
                    a.y*b.z - a.z*b.y,
                    a.z*b.x - a.x*b.z,
                    a.x*b.y - a.y*b.x
                    );
            }

            // Scalar multiplication
            inline Vector3 operator *(float k, const Vector3 &v) {
                return Vector3(k*v.x, k*v.y, k*v.z);
            }

        }
    }
}






